In recent times, the field of agriculture has been in urgent need of modernizing, since the amount of manual work people need to put in to check if plants are growing correctly is still highly extensive. Despite several advances in agricultural technology, people working in the agricultural industry still need to have the ability to sort and recognize different plants and weeds, which takes a lot of time and effort in the long term. The potential is ripe for this trillion-dollar industry to be greatly impacted by technological innovations that cut down on the requirement for manual labor, and this is where Artificial Intelligence can actually benefit the workers in this field, as the time and energy required to identify plant seedlings will be greatly shortened by the use of AI and Deep Learning. The ability to do so far more efficiently and even more effectively than experienced manual labor, could lead to better crop yields, the freeing up of human inolvement for higher-order agricultural decision making, and in the long term will result in more sustainable environmental practices in agriculture as well.
The aim of this project is to Build a Convolutional Neural Netowrk to classify plant seedlings into their respective categories.
The Aarhus University Signal Processing group, in collaboration with the University of Southern Denmark, has recently released a dataset containing images of unique plants belonging to 12 different species.
Due to the large volume of data, the images were converted to the images.npy file and the labels are also put into Labels.csv, so that you can work on the data/project seamlessly without having to worry about the high data volume.
The goal of the project is to create a classifier capable of determining a plant's species from an image.
List of Species
#Installing the libraries with the specified version.
#uncomment and run the following line if Google Colab is being used
!pip install tensorflow==2.15.0 scikit-learn==1.2.2 seaborn==0.13.1 matplotlib==3.7.1 numpy==1.25.2 pandas==1.5.3 opencv-python==4.8.0.76 -q --user
!pip install --no-index -f https://github.com/dreoporto/ptmlib/releases ptmlib
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 475.2/475.2 MB 3.0 MB/s eta 0:00:00 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.6/9.6 MB 29.7 MB/s eta 0:00:00 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 18.2/18.2 MB 21.3 MB/s eta 0:00:00 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.1/12.1 MB 34.8 MB/s eta 0:00:00 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 61.7/61.7 MB 7.4 MB/s eta 0:00:00 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.7/1.7 MB 24.5 MB/s eta 0:00:00 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.0/1.0 MB 21.3 MB/s eta 0:00:00 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.5/5.5 MB 54.3 MB/s eta 0:00:00 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 442.0/442.0 kB 11.0 MB/s eta 0:00:00 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 77.9/77.9 kB 5.4 MB/s eta 0:00:00 WARNING: The scripts f2py, f2py3 and f2py3.10 are installed in '/root/.local/bin' which is not on PATH. Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. WARNING: The script tensorboard is installed in '/root/.local/bin' which is not on PATH. Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. WARNING: The scripts estimator_ckpt_converter, import_pb_to_tensorboard, saved_model_cli, tensorboard, tf_upgrade_v2, tflite_convert, toco and toco_from_protos are installed in '/root/.local/bin' which is not on PATH. Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. cudf-cu12 24.4.1 requires pandas<2.2.2dev0,>=2.0, but you have pandas 1.5.3 which is incompatible. google-colab 1.0.0 requires pandas==2.1.4, but you have pandas 1.5.3 which is incompatible. mizani 0.11.4 requires pandas>=2.1.0, but you have pandas 1.5.3 which is incompatible. pandas-stubs 2.1.4.231227 requires numpy>=1.26.0; python_version < "3.13", but you have numpy 1.25.2 which is incompatible. plotnine 0.13.6 requires pandas<3.0.0,>=2.1.0, but you have pandas 1.5.3 which is incompatible. tensorstore 0.1.65 requires ml-dtypes>=0.3.1, but you have ml-dtypes 0.2.0 which is incompatible. tf-keras 2.17.0 requires tensorflow<2.18,>=2.17, but you have tensorflow 2.15.0 which is incompatible. xarray 2024.9.0 requires pandas>=2.1, but you have pandas 1.5.3 which is incompatible. Looking in links: https://github.com/dreoporto/ptmlib/releases Collecting ptmlib Downloading https://github.com/dreoporto/ptmlib/releases/download/v0.1.10/ptmlib-0.1.10-py3-none-any.whl (93 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 93.6/93.6 kB 2.9 MB/s eta 0:00:00 Installing collected packages: ptmlib Successfully installed ptmlib-0.1.10
# Installing the libraries with the specified version.
# uncomment and run the following lines if Jupyter Notebook is being used
#!pip install tensorflow==2.13.0 scikit-learn==1.2.2 seaborn==0.11.1 matplotlib==3.3.4 numpy==1.24.3 pandas==1.5.2 opencv-python==4.8.0.76 -q --user
Note: After running the above cell, kindly restart the notebook kernel and run all cells sequentially from the start again.
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
import cv2
import seaborn as sns
import plotly.express as px
import plotly.io as pio
pio.renderers.default = 'notebook'
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D, BatchNormalization, SpatialDropout2D
from tensorflow.keras.optimizers import Adam, SGD
from sklearn import preprocessing
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
from sklearn import metrics
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from tensorflow.keras import backend
from keras.callbacks import ReduceLROnPlateau, EarlyStopping
from keras.regularizers import l1, l2, l1_l2
from keras.layers import Dropout
import random
import ptmlib.model_tools as modt
import warnings
warnings.filterwarnings('ignore')
# Uncomment and run the below code if you are using google colab
from google.colab import drive
drive.mount('/content/drive')
labels = pd.read_csv("/content/drive/MyDrive/Labels(1).csv")
images = np.load("/content/drive/MyDrive/images(1).npy")
Mounted at /content/drive
labels.head()
| Label | |
|---|---|
| 0 | Small-flowered Cranesbill |
| 1 | Small-flowered Cranesbill |
| 2 | Small-flowered Cranesbill |
| 3 | Small-flowered Cranesbill |
| 4 | Small-flowered Cranesbill |
# check number of rows and columns
print("labels: ", labels.shape)
print("images: ", images.shape)
labels: (4750, 1) images: (4750, 128, 128, 3)
Observations:
Labels has 4,750 rows and 1 column
def plot_images(images, labels):
num_images = min(12, len(images)) # Total number of images to display
rows, cols = 3, 4
fig, axes = plt.subplots(rows, cols, figsize=(10, 8))
axes = axes.flatten()
# Convert labels to a list if it's a pandas Series or DataFrame
if hasattr(labels, 'values'):
labels = labels.values
for i, ax in enumerate(axes):
if i < num_images:
random_index = np.random.randint(0, len(images))
ax.imshow(images[random_index])
# Handle different label types
if isinstance(labels, (list, np.ndarray)):
label = labels[random_index]
elif isinstance(labels, dict):
label = labels.get(random_index, "Unknown")
else:
label = "Unknown"
ax.set_title(str(label))
ax.axis('off')
else:
ax.axis('off') # Turn off unused subplots
plt.tight_layout()
plt.show()
plot_images(images, labels)
# check distribution of target variable by species name
# checking frequency
sns.countplot(x=labels['Label'])
plt.xticks(rotation='vertical')
plt.xticks(rotation=45, ha='right')
plt.show();
Observations:
Some species have lower frequency such as Common wheat and Shepherds Purse.
# Plot image width and height
img_widths, img_heights = [], []
for img in images:
h, w, _ = img.shape
img_widths.append(w)
img_heights.append(h)
# plot distribution of widths and heights
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.hist(img_widths, bins=50, color='orange', alpha=0.7)
plt.title("Distribution of Image Width")
plt.subplot(1, 2, 2)
plt.hist(img_heights, bins=50, color='purple', alpha=0.7)
plt.title("Distribution of Image Height")
plt.show()
Observations: All images have the same width and height. There is only one height and width.
random_img = images[np.random.randint(0, len(images))]
fig, axs = plt.subplots(1, 3, figsize=(12, 6))
colors = ['red', 'green', 'blue']
[ax.hist(random_img[:,:,i].ravel(), bins=50, color=c, alpha=0.6) for i, (ax, c) in enumerate(zip(axs, colors))]
for ax, color in zip(axs, colors):
ax.set_title(f"{color.capitalize()} Pixel Value Distribution")
plt.tight_layout()
plt.show()
Observations:
The distribution is consistent with all 3 color channels.
THey all are left skewed distribution closer to 0 meaning that the images have darker shades of the 3 colors. Like darker shade of red, green, and blue.
# check for grayscale images
def is_grayscale(img):
return np.allclose(img[:,:,0], img[:,:,1]) and np.allclose(img[:,:,1], img[:,:,2])
gray_images = sum(is_grayscale(img) for img in images)
print(f"Total grayscale images: {gray_images}")
Total grayscale images: 0
Observations: There are 0 grayscale images. All images are in color.
# converting images from BGR to RGB using cvtColor function from OpenCV
images = [cv2.cvtColor(img, cv2.COLOR_BGR2RGB) for img in images]
plot_images(images, labels)
As the size of the images is large, it may be computationally expensive to train on these larger images; therefore, it is preferable to reduce the image size from 128 to 64.
# downscale the images
images_decreased = []
height = 64
width = 64
dimensions = (width, height)
for i in range(len(images)):
images_decreased.append(cv2.resize(images[i], dimensions, interpolation=cv2.INTER_LINEAR))
# plot original and downscaled images
count = 2
Titles = ["Original", "Resized"]
image = [images[52], images_decreased[52]]
for i in range(count):
plt.subplot(1, count, i + 1)
plt.title(Titles[i])
plt.imshow(image[i])
plt.show();
Split the dataset
X_temp, X_test, y_temp, y_test = train_test_split(
np.array(images_decreased), labels, test_size=0.1, random_state=42,
stratify=labels
)
X_train, X_val, y_train, y_val = train_test_split(
X_temp, y_temp, test_size=0.1, random_state=42, stratify=y_temp
)
# check shapes of split datasets
print(X_train.shape, y_train.shape)
print(X_val.shape, y_val.shape)
print(X_test.shape, y_test.shape)
(3847, 64, 64, 3) (3847, 1) (428, 64, 64, 3) (428, 1) (475, 64, 64, 3) (475, 1)
# convert labels from names to one hot vectors
# using label binarizer instead of onehotencoding it works similar to it
enc = LabelBinarizer()
y_train_encoded = enc.fit_transform(y_train)
y_val_encoded = enc.transform(y_val)
y_test_encoded = enc.transform(y_test)
# check shape of encoded labels
y_train_encoded.shape, y_val_encoded.shape, y_test_encoded.shape
((3847, 12), (428, 12), (475, 12))
# normalize data to range of 0 to 1
X_train_normalized = X_train.astype('float32') / 255.0
X_val_normalized = X_val.astype('float32') / 255.0
X_test_normalized = X_test.astype('float32') / 255.0
backend.clear_session()
# fixing the seed for random number generators
np.random.seed(42)
random.seed(42)
tf.random.set_seed(42)
model1 = Sequential()
# Input_shape denotes input image dimension of images
model1.add(Conv2D(128, (3, 3), activation='relu', padding="same", input_shape=(64, 64, 3)))
model1.add(MaxPooling2D((2, 2), padding='same'))
model1.add(Conv2D(64, (3, 3), activation='relu', padding="same"))
model1.add(MaxPooling2D((2, 2), padding='same'))
model1.add(Conv2D(32, (3, 3), activation='relu', padding="same"))
model1.add(MaxPooling2D((2, 2), padding='same'))
model1.add(Flatten())
model1.add(Dense(16, activation='relu'))
model1.add(Dropout(0.3))
model1.add(Dense(12, activation='softmax'))
opt = Adam()
# Complete the code to Compile the model using suitable metric for loss fucntion
model1.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Complete the code to generate the summary of the model
model1.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 64, 64, 128) │ 3,584 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 32, 32, 128) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_1 (Conv2D) │ (None, 32, 32, 64) │ 73,792 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 16, 16, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_2 (Conv2D) │ (None, 16, 16, 32) │ 18,464 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 8, 8, 32) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ flatten (Flatten) │ (None, 2048) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense (Dense) │ (None, 16) │ 32,784 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout (Dropout) │ (None, 16) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_1 (Dense) │ (None, 12) │ 204 │ └──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
Total params: 128,828 (503.23 KB)
Trainable params: 128,828 (503.23 KB)
Non-trainable params: 0 (0.00 B)
# Fit the model on training data for 100 epochs
fit_model_1 = lambda model_1, x, y, validation_data, epochs: model_1.fit(
x, y, validation_data=validation_data, epochs=epochs)
model_1_file = "model_1"
model_1, history = modt.load_or_fit_model(
model1, model_1_file, x=X_train_normalized, y=y_train_encoded,
validation_data=(X_val_normalized, y_val_encoded),
epochs=30,
fit_model_function=fit_model_1,
metrics=['accuracy']
)
Start Time: Sat Sep 21 02:06:33 2024 Epoch 1/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 12s 48ms/step - accuracy: 0.1139 - loss: 2.4785 - val_accuracy: 0.1379 - val_loss: 2.4477 Epoch 2/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 13s 21ms/step - accuracy: 0.1811 - loss: 2.3965 - val_accuracy: 0.3435 - val_loss: 2.0139 Epoch 3/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 2s 13ms/step - accuracy: 0.3036 - loss: 2.0303 - val_accuracy: 0.3995 - val_loss: 1.7669 Epoch 4/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.3254 - loss: 1.8773 - val_accuracy: 0.4860 - val_loss: 1.7206 Epoch 5/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.3544 - loss: 1.7882 - val_accuracy: 0.5140 - val_loss: 1.4750 Epoch 6/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.3862 - loss: 1.6602 - val_accuracy: 0.5374 - val_loss: 1.4392 Epoch 7/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.4092 - loss: 1.5812 - val_accuracy: 0.5444 - val_loss: 1.3900 Epoch 8/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.4268 - loss: 1.5491 - val_accuracy: 0.5794 - val_loss: 1.2488 Epoch 9/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.4492 - loss: 1.4749 - val_accuracy: 0.5607 - val_loss: 1.3468 Epoch 10/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.4541 - loss: 1.4585 - val_accuracy: 0.5771 - val_loss: 1.2685 Epoch 11/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.4768 - loss: 1.3937 - val_accuracy: 0.5794 - val_loss: 1.2709 Epoch 12/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.4556 - loss: 1.4015 - val_accuracy: 0.6238 - val_loss: 1.2262 Epoch 13/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.4788 - loss: 1.3744 - val_accuracy: 0.6215 - val_loss: 1.1239 Epoch 14/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.4909 - loss: 1.3567 - val_accuracy: 0.5888 - val_loss: 1.2911 Epoch 15/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.4964 - loss: 1.3588 - val_accuracy: 0.5794 - val_loss: 1.2603 Epoch 16/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.5095 - loss: 1.3034 - val_accuracy: 0.6028 - val_loss: 1.2158 Epoch 17/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.5210 - loss: 1.2769 - val_accuracy: 0.6215 - val_loss: 1.1954 Epoch 18/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.5186 - loss: 1.2914 - val_accuracy: 0.6542 - val_loss: 1.0492 Epoch 19/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.5385 - loss: 1.2474 - val_accuracy: 0.6308 - val_loss: 1.1215 Epoch 20/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.5443 - loss: 1.2187 - val_accuracy: 0.6706 - val_loss: 1.0214 Epoch 21/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.5529 - loss: 1.1798 - val_accuracy: 0.6729 - val_loss: 1.0062 Epoch 22/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 11ms/step - accuracy: 0.5603 - loss: 1.1416 - val_accuracy: 0.6659 - val_loss: 0.9931 Epoch 23/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 2s 9ms/step - accuracy: 0.5735 - loss: 1.1084 - val_accuracy: 0.6822 - val_loss: 0.9523 Epoch 24/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.5785 - loss: 1.1042 - val_accuracy: 0.6636 - val_loss: 0.9559 Epoch 25/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.5606 - loss: 1.1153 - val_accuracy: 0.6869 - val_loss: 0.9607 Epoch 26/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.5748 - loss: 1.0654 - val_accuracy: 0.6799 - val_loss: 0.9342 Epoch 27/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.5977 - loss: 1.0373 - val_accuracy: 0.6612 - val_loss: 1.0198 Epoch 28/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.5913 - loss: 1.0578 - val_accuracy: 0.7033 - val_loss: 0.9443 Epoch 29/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 9ms/step - accuracy: 0.6166 - loss: 1.0133 - val_accuracy: 0.7266 - val_loss: 0.9133 Epoch 30/30 121/121 ━━━━━━━━━━━━━━━━━━━━ 1s 10ms/step - accuracy: 0.6207 - loss: 0.9880 - val_accuracy: 0.7056 - val_loss: 0.9211 End Time: Sat Sep 21 02:07:36 2024 Elapsed seconds: 62.3865 (1.04 minutes)
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.
Saving new model file: model_1.h5 Saved image: accuracy-model_1.png
Saved image: loss-model_1.png
import pandas as pd
import matplotlib.pyplot as plt
# Capturing learning history per epoch
hist = pd.DataFrame(history.history)
hist['epoch'] = hist.index + 1
# Create the plot
plt.figure(figsize=(12, 6))
# Plot accuracy
plt.plot(hist['epoch'], hist['accuracy'], label='Train Accuracy')
plt.plot(hist['epoch'], hist['val_accuracy'], label='Validation Accuracy')
# Plot loss
plt.plot(hist['epoch'], hist['loss'], label='Train Loss')
plt.plot(hist['epoch'], hist['val_loss'], label='Validation Loss')
# Add a horizontal line at y=1
plt.axhline(y=1, color='gray', linestyle=':', linewidth=2)
# Customize the plot
plt.title("Train and Validation Losses Over Epochs")
plt.xlabel("Epochs")
plt.ylabel("Accuracy/Loss")
plt.legend(title="Metrics", loc='center left', bbox_to_anchor=(1, 0.5))
plt.grid(True)
# Adjust layout to prevent cutting off labels
plt.tight_layout()
# Show the plot
plt.show()
# Evaluate the model on X_test and y_test
results = model_1.evaluate(X_test_normalized, y_test_encoded)
print(f"Test loss: {results[0]}")
print(f"Test accuracy: {results[1]}")
15/15 ━━━━━━━━━━━━━━━━━━━━ 0s 13ms/step - accuracy: 0.7589 - loss: 0.8205 Test loss: 0.9362202286720276 Test accuracy: 0.7221052646636963
accuracy = model_1.evaluate(X_test_normalized, y_test_encoded, verbose=1)
15/15 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.7589 - loss: 0.8205
y_pred = model_1.predict(X_test_normalized)
15/15 ━━━━━━━━━━━━━━━━━━━━ 1s 62ms/step
# Obtaining the categorical values from y_test_encoded and y_pred
y_pred_arg = np.argmax(y_pred, axis=1)
y_test_arg = np.argmax(y_test_encoded, axis=1)
# Plotting the confusion Matrix using confusion matrix() function which is also predefined
confusion_matrix = tf.math.confusion_matrix(y_test_arg, y_pred_arg)
f, ax = plt.subplots(figsize=(12, 12))
sns.heatmap(
confusion_matrix,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
# Setting the labels to both the axes
ax.set_xlabel('Predicted labels');
ax.set_ylabel('True labels');
ax.set_title('Confusion Matrix');
ax.xaxis.set_ticklabels(list(enc.classes_), rotation=40)
ax.yaxis.set_ticklabels(list(enc.classes_), rotation=20)
plt.show()
# Printing the classification report
cr = metrics.classification_report(y_test_arg, y_pred_arg)
print(cr)
precision recall f1-score support
0 0.00 0.00 0.00 26
1 0.69 0.90 0.78 39
2 0.79 0.38 0.51 29
3 0.82 0.84 0.83 61
4 0.88 0.32 0.47 22
5 0.74 0.81 0.77 48
6 0.63 0.95 0.76 65
7 0.86 0.55 0.67 22
8 0.62 0.85 0.72 52
9 0.75 0.65 0.70 23
10 0.87 0.82 0.85 50
11 0.72 0.68 0.70 38
accuracy 0.72 475
macro avg 0.70 0.65 0.65 475
weighted avg 0.70 0.72 0.69 475
Reducing the Learning Rate:
Hint: Use ReduceLRonPlateau() function that will be used to decrease the learning rate by some factor, if the loss is not decreasing for some time. This may start decreasing the loss at a smaller learning rate. There is a possibility that the loss may still not decrease. This may lead to executing the learning rate reduction again in an attempt to achieve a lower loss.
# Code to monitor val_accuracy
learning_rate_reduction = ReduceLROnPlateau(
monitor='val_accuracy',
patience=3,
verbose=1,
factor=0.5,
min_lr=0.00001
)
Remember, data augmentation should not be used in the validation/test data set.
# Code to monitor val_accuracy
learning_rate_reduction = ReduceLROnPlateau(
monitor='val_accuracy',
patience=3,
verbose=1,
factor=0.5,
min_lr=0.00001
)
# Creating a datagen augmentation object
train_datagen = ImageDataGenerator(
rotation_range=45,
shear_range=0.15,
zoom_range=0.15,
width_shift_range=0.15,
height_shift_range=0.15,
vertical_flip=True,
horizontal_flip=True,
# brightness_range=[0.8, 1.2], # made things much worse
fill_mode='nearest'
)
# intializing a sequential model - model 2
model2 = Sequential()
# Input_shape denotes input image dimension images
model2.add(
Conv2D(64, (3, 3), activation='relu', padding="same", kernel_initializer='he_normal', input_shape=(64, 64, 3))
)
model2.add(BatchNormalization())
model2.add(Conv2D(64, (3, 3), activation='relu', padding="same", kernel_initializer='he_normal'))
model2.add(BatchNormalization())
model2.add(MaxPooling2D((2, 2), padding='same'))
model2.add(Dropout(0.3))
model2.add(Conv2D(256, (5, 5), activation='relu', padding="same", kernel_initializer='he_normal'))
model2.add(BatchNormalization())
model2.add(Conv2D(256, (3, 3), activation='relu', padding="same", kernel_initializer='he_normal'))
model2.add(BatchNormalization())
model2.add(MaxPooling2D((2, 2), padding='same'))
model2.add(Dropout(0.4))
model2.add(Conv2D(32, (3, 3), activation='relu', padding="same", kernel_initializer='he_normal'))
model2.add(BatchNormalization())
model2.add(MaxPooling2D((2, 2), padding='same'))
model2.add(SpatialDropout2D(0.3))
# flattening the output of the conv layer after max pooling to make it ready for creating dense connections
model2.add(Flatten())
model2.add(Dense(128, activation='relu', kernel_regularizer='l2'))
model2.add(Dropout(0.5))
model2.add(Dense(32, activation='relu'))
model2.add(Dense(12, activation='softmax'))
opt = Adam(learning_rate=0.001)
model2.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generating the summary of the model
model2.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 64, 64, 64) │ 1,792 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ batch_normalization │ (None, 64, 64, 64) │ 256 │ │ (BatchNormalization) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_1 (Conv2D) │ (None, 64, 64, 64) │ 36,928 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ batch_normalization_1 │ (None, 64, 64, 64) │ 256 │ │ (BatchNormalization) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 32, 32, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout (Dropout) │ (None, 32, 32, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_2 (Conv2D) │ (None, 32, 32, 256) │ 409,856 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ batch_normalization_2 │ (None, 32, 32, 256) │ 1,024 │ │ (BatchNormalization) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_3 (Conv2D) │ (None, 32, 32, 256) │ 590,080 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ batch_normalization_3 │ (None, 32, 32, 256) │ 1,024 │ │ (BatchNormalization) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 16, 16, 256) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout_1 (Dropout) │ (None, 16, 16, 256) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_4 (Conv2D) │ (None, 16, 16, 32) │ 73,760 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ batch_normalization_4 │ (None, 16, 16, 32) │ 128 │ │ (BatchNormalization) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 8, 8, 32) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ spatial_dropout2d (SpatialDropout2D) │ (None, 8, 8, 32) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ flatten (Flatten) │ (None, 2048) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense (Dense) │ (None, 128) │ 262,272 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout_2 (Dropout) │ (None, 128) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_1 (Dense) │ (None, 32) │ 4,128 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_2 (Dense) │ (None, 12) │ 396 │ └──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
Total params: 1,381,900 (5.27 MB)
Trainable params: 1,380,556 (5.27 MB)
Non-trainable params: 1,344 (5.25 KB)
# Fit the model on training data for 120 epochs
epochs = 120
# Batch size
batch_size = 32
early_stopping = EarlyStopping(monitor='val_loss', patience=10, verbose=1)
# Fit the model on training data for 100 epochs
fit_model_2 = lambda model_2, x, y, validation_data, epochs: model_2.fit(
train_datagen.flow(x, y, batch_size=batch_size, shuffle=False),
steps_per_epoch=X_train_normalized.shape[0] // batch_size,
callbacks=[learning_rate_reduction, early_stopping],
validation_data=validation_data,
epochs=epochs,
)
model_2_file = "model_2"
model_2, history2 = modt.load_or_fit_model(
model2, model_2_file, x=X_train_normalized, y=y_train_encoded,
validation_data=(X_val_normalized, y_val_encoded),
epochs=epochs,
fit_model_function=fit_model_2,
metrics=['accuracy']
)
Start Time: Sat Sep 21 02:20:00 2024 Epoch 1/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 40s 173ms/step - accuracy: 0.1565 - loss: 4.9168 - val_accuracy: 0.1402 - val_loss: 5.1385 - learning_rate: 0.0010 Epoch 2/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.2812 - loss: 3.6709 - val_accuracy: 0.1379 - val_loss: 5.4157 - learning_rate: 0.0010 Epoch 3/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 8s 67ms/step - accuracy: 0.3496 - loss: 3.2643 - val_accuracy: 0.1822 - val_loss: 5.1073 - learning_rate: 0.0010 Epoch 4/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.3438 - loss: 2.9056 - val_accuracy: 0.1776 - val_loss: 5.1556 - learning_rate: 0.0010 Epoch 5/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 55ms/step - accuracy: 0.4002 - loss: 2.6424 - val_accuracy: 0.2290 - val_loss: 2.9600 - learning_rate: 0.0010 Epoch 6/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.4688 - loss: 2.1809 - val_accuracy: 0.2407 - val_loss: 2.8680 - learning_rate: 0.0010 Epoch 7/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 11s 62ms/step - accuracy: 0.4701 - loss: 2.1832 - val_accuracy: 0.4416 - val_loss: 2.3440 - learning_rate: 0.0010 Epoch 8/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.4062 - loss: 2.1582 - val_accuracy: 0.5000 - val_loss: 2.1913 - learning_rate: 0.0010 Epoch 9/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 62ms/step - accuracy: 0.5272 - loss: 1.9706 - val_accuracy: 0.4953 - val_loss: 1.9300 - learning_rate: 0.0010 Epoch 10/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.5000 - loss: 1.9836 - val_accuracy: 0.5350 - val_loss: 1.8591 - learning_rate: 0.0010 Epoch 11/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 11s 69ms/step - accuracy: 0.5364 - loss: 1.8352 - val_accuracy: 0.6963 - val_loss: 1.3309 - learning_rate: 0.0010 Epoch 12/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.5938 - loss: 1.7457 - val_accuracy: 0.7009 - val_loss: 1.3460 - learning_rate: 0.0010 Epoch 13/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 7s 56ms/step - accuracy: 0.6139 - loss: 1.6119 - val_accuracy: 0.5607 - val_loss: 1.6981 - learning_rate: 0.0010 Epoch 14/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.5312 - loss: 1.7790 - val_accuracy: 0.5584 - val_loss: 1.7256 - learning_rate: 0.0010 Epoch 15/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 67ms/step - accuracy: 0.6072 - loss: 1.5740 Epoch 15: ReduceLROnPlateau reducing learning rate to 0.0005000000237487257. 120/120 ━━━━━━━━━━━━━━━━━━━━ 8s 68ms/step - accuracy: 0.6072 - loss: 1.5739 - val_accuracy: 0.6215 - val_loss: 1.5952 - learning_rate: 0.0010 Epoch 16/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.6875 - loss: 1.3253 - val_accuracy: 0.6238 - val_loss: 1.5797 - learning_rate: 5.0000e-04 Epoch 17/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 65ms/step - accuracy: 0.6535 - loss: 1.4472 - val_accuracy: 0.7780 - val_loss: 1.0500 - learning_rate: 5.0000e-04 Epoch 18/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.6875 - loss: 1.1547 - val_accuracy: 0.7827 - val_loss: 1.0437 - learning_rate: 5.0000e-04 Epoch 19/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 8s 62ms/step - accuracy: 0.6964 - loss: 1.2832 - val_accuracy: 0.7336 - val_loss: 1.1334 - learning_rate: 5.0000e-04 Epoch 20/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.6562 - loss: 1.2319 - val_accuracy: 0.7196 - val_loss: 1.1513 - learning_rate: 5.0000e-04 Epoch 21/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 11s 69ms/step - accuracy: 0.7016 - loss: 1.1599 - val_accuracy: 0.7874 - val_loss: 0.9358 - learning_rate: 5.0000e-04 Epoch 22/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.6875 - loss: 1.1194 - val_accuracy: 0.7991 - val_loss: 0.9124 - learning_rate: 5.0000e-04 Epoch 23/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 7s 59ms/step - accuracy: 0.7277 - loss: 1.0852 - val_accuracy: 0.8598 - val_loss: 0.8055 - learning_rate: 5.0000e-04 Epoch 24/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.7500 - loss: 1.1991 - val_accuracy: 0.8575 - val_loss: 0.8064 - learning_rate: 5.0000e-04 Epoch 25/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 56ms/step - accuracy: 0.7439 - loss: 1.0035 - val_accuracy: 0.7757 - val_loss: 0.9782 - learning_rate: 5.0000e-04 Epoch 26/120 1/120 ━━━━━━━━━━━━━━━━━━━━ 4s 39ms/step - accuracy: 0.8438 - loss: 0.9120 Epoch 26: ReduceLROnPlateau reducing learning rate to 0.0002500000118743628. 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8438 - loss: 0.9120 - val_accuracy: 0.7640 - val_loss: 0.9936 - learning_rate: 5.0000e-04 Epoch 27/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 69ms/step - accuracy: 0.7566 - loss: 0.9672 - val_accuracy: 0.8762 - val_loss: 0.6740 - learning_rate: 2.5000e-04 Epoch 28/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.6875 - loss: 1.0049 - val_accuracy: 0.8785 - val_loss: 0.6706 - learning_rate: 2.5000e-04 Epoch 29/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 59ms/step - accuracy: 0.7861 - loss: 0.8592 - val_accuracy: 0.8505 - val_loss: 0.7399 - learning_rate: 2.5000e-04 Epoch 30/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.8125 - loss: 0.6983 - val_accuracy: 0.8435 - val_loss: 0.7371 - learning_rate: 2.5000e-04 Epoch 31/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 66ms/step - accuracy: 0.7789 - loss: 0.8536 Epoch 31: ReduceLROnPlateau reducing learning rate to 0.0001250000059371814. 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 68ms/step - accuracy: 0.7789 - loss: 0.8537 - val_accuracy: 0.8785 - val_loss: 0.6224 - learning_rate: 2.5000e-04 Epoch 32/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8750 - loss: 0.7335 - val_accuracy: 0.8762 - val_loss: 0.6208 - learning_rate: 1.2500e-04 Epoch 33/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 69ms/step - accuracy: 0.8074 - loss: 0.7877 - val_accuracy: 0.8808 - val_loss: 0.5917 - learning_rate: 1.2500e-04 Epoch 34/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8438 - loss: 0.7422 - val_accuracy: 0.8808 - val_loss: 0.5893 - learning_rate: 1.2500e-04 Epoch 35/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 7s 56ms/step - accuracy: 0.7901 - loss: 0.7886 - val_accuracy: 0.8925 - val_loss: 0.5576 - learning_rate: 1.2500e-04 Epoch 36/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8125 - loss: 0.8295 - val_accuracy: 0.8925 - val_loss: 0.5571 - learning_rate: 1.2500e-04 Epoch 37/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 69ms/step - accuracy: 0.8154 - loss: 0.7267 - val_accuracy: 0.8762 - val_loss: 0.5801 - learning_rate: 1.2500e-04 Epoch 38/120 1/120 ━━━━━━━━━━━━━━━━━━━━ 4s 39ms/step - accuracy: 0.8438 - loss: 0.7554 Epoch 38: ReduceLROnPlateau reducing learning rate to 6.25000029685907e-05. 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8438 - loss: 0.7554 - val_accuracy: 0.8715 - val_loss: 0.5790 - learning_rate: 1.2500e-04 Epoch 39/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 66ms/step - accuracy: 0.8229 - loss: 0.7214 - val_accuracy: 0.8949 - val_loss: 0.5240 - learning_rate: 6.2500e-05 Epoch 40/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.8438 - loss: 0.6110 - val_accuracy: 0.8972 - val_loss: 0.5242 - learning_rate: 6.2500e-05 Epoch 41/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 56ms/step - accuracy: 0.8285 - loss: 0.6880 - val_accuracy: 0.9089 - val_loss: 0.5137 - learning_rate: 6.2500e-05 Epoch 42/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8438 - loss: 0.7539 - val_accuracy: 0.9089 - val_loss: 0.5131 - learning_rate: 6.2500e-05 Epoch 43/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 71ms/step - accuracy: 0.8283 - loss: 0.6614 - val_accuracy: 0.9065 - val_loss: 0.5288 - learning_rate: 6.2500e-05 Epoch 44/120 1/120 ━━━━━━━━━━━━━━━━━━━━ 4s 36ms/step - accuracy: 0.7812 - loss: 0.6660 Epoch 44: ReduceLROnPlateau reducing learning rate to 3.125000148429535e-05. 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.7812 - loss: 0.6660 - val_accuracy: 0.9065 - val_loss: 0.5316 - learning_rate: 6.2500e-05 Epoch 45/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 7s 57ms/step - accuracy: 0.8331 - loss: 0.6467 - val_accuracy: 0.9042 - val_loss: 0.5132 - learning_rate: 3.1250e-05 Epoch 46/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.8125 - loss: 0.7055 - val_accuracy: 0.9042 - val_loss: 0.5142 - learning_rate: 3.1250e-05 Epoch 47/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 63ms/step - accuracy: 0.8395 - loss: 0.6390 Epoch 47: ReduceLROnPlateau reducing learning rate to 1.5625000742147677e-05. 120/120 ━━━━━━━━━━━━━━━━━━━━ 8s 65ms/step - accuracy: 0.8395 - loss: 0.6391 - val_accuracy: 0.8972 - val_loss: 0.5082 - learning_rate: 3.1250e-05 Epoch 48/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.9062 - loss: 0.5059 - val_accuracy: 0.8972 - val_loss: 0.5080 - learning_rate: 1.5625e-05 Epoch 49/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 69ms/step - accuracy: 0.8431 - loss: 0.6318 - val_accuracy: 0.9089 - val_loss: 0.5114 - learning_rate: 1.5625e-05 Epoch 50/120 1/120 ━━━━━━━━━━━━━━━━━━━━ 4s 39ms/step - accuracy: 0.7500 - loss: 0.9330 Epoch 50: ReduceLROnPlateau reducing learning rate to 1e-05. 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.7500 - loss: 0.9330 - val_accuracy: 0.9089 - val_loss: 0.5108 - learning_rate: 1.5625e-05 Epoch 51/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 7s 57ms/step - accuracy: 0.8307 - loss: 0.6318 - val_accuracy: 0.9019 - val_loss: 0.4953 - learning_rate: 1.0000e-05 Epoch 52/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8125 - loss: 0.6105 - val_accuracy: 0.9019 - val_loss: 0.4953 - learning_rate: 1.0000e-05 Epoch 53/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 69ms/step - accuracy: 0.8494 - loss: 0.6125 - val_accuracy: 0.9159 - val_loss: 0.4816 - learning_rate: 1.0000e-05 Epoch 54/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8125 - loss: 0.6365 - val_accuracy: 0.9159 - val_loss: 0.4816 - learning_rate: 1.0000e-05 Epoch 55/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 72ms/step - accuracy: 0.8465 - loss: 0.6115 - val_accuracy: 0.9112 - val_loss: 0.4886 - learning_rate: 1.0000e-05 Epoch 56/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.8438 - loss: 0.4870 - val_accuracy: 0.9112 - val_loss: 0.4884 - learning_rate: 1.0000e-05 Epoch 57/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 7s 56ms/step - accuracy: 0.8319 - loss: 0.6441 - val_accuracy: 0.9112 - val_loss: 0.4845 - learning_rate: 1.0000e-05 Epoch 58/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.9688 - loss: 0.3655 - val_accuracy: 0.9112 - val_loss: 0.4849 - learning_rate: 1.0000e-05 Epoch 59/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 11s 67ms/step - accuracy: 0.8513 - loss: 0.5920 - val_accuracy: 0.9159 - val_loss: 0.4776 - learning_rate: 1.0000e-05 Epoch 60/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8750 - loss: 0.5758 - val_accuracy: 0.9136 - val_loss: 0.4776 - learning_rate: 1.0000e-05 Epoch 61/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 67ms/step - accuracy: 0.8371 - loss: 0.6098 - val_accuracy: 0.9112 - val_loss: 0.4779 - learning_rate: 1.0000e-05 Epoch 62/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.9688 - loss: 0.4445 - val_accuracy: 0.9136 - val_loss: 0.4777 - learning_rate: 1.0000e-05 Epoch 63/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 56ms/step - accuracy: 0.8338 - loss: 0.6114 - val_accuracy: 0.9159 - val_loss: 0.4766 - learning_rate: 1.0000e-05 Epoch 64/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.7500 - loss: 0.6653 - val_accuracy: 0.9136 - val_loss: 0.4766 - learning_rate: 1.0000e-05 Epoch 65/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 57ms/step - accuracy: 0.8315 - loss: 0.6285 - val_accuracy: 0.9159 - val_loss: 0.4723 - learning_rate: 1.0000e-05 Epoch 66/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.7812 - loss: 0.8271 - val_accuracy: 0.9159 - val_loss: 0.4716 - learning_rate: 1.0000e-05 Epoch 67/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 11s 66ms/step - accuracy: 0.8381 - loss: 0.6282 - val_accuracy: 0.9112 - val_loss: 0.4736 - learning_rate: 1.0000e-05 Epoch 68/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8750 - loss: 0.5477 - val_accuracy: 0.9112 - val_loss: 0.4732 - learning_rate: 1.0000e-05 Epoch 69/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 68ms/step - accuracy: 0.8468 - loss: 0.5915 - val_accuracy: 0.9042 - val_loss: 0.4821 - learning_rate: 1.0000e-05 Epoch 70/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8125 - loss: 0.5617 - val_accuracy: 0.9042 - val_loss: 0.4824 - learning_rate: 1.0000e-05 Epoch 71/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 7s 56ms/step - accuracy: 0.8498 - loss: 0.5871 - val_accuracy: 0.9112 - val_loss: 0.4786 - learning_rate: 1.0000e-05 Epoch 72/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8125 - loss: 0.7527 - val_accuracy: 0.9112 - val_loss: 0.4784 - learning_rate: 1.0000e-05 Epoch 73/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 71ms/step - accuracy: 0.8322 - loss: 0.6131 - val_accuracy: 0.9089 - val_loss: 0.4714 - learning_rate: 1.0000e-05 Epoch 74/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8750 - loss: 0.5683 - val_accuracy: 0.9089 - val_loss: 0.4716 - learning_rate: 1.0000e-05 Epoch 75/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 7s 57ms/step - accuracy: 0.8416 - loss: 0.6145 - val_accuracy: 0.9136 - val_loss: 0.4665 - learning_rate: 1.0000e-05 Epoch 76/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8750 - loss: 0.5183 - val_accuracy: 0.9136 - val_loss: 0.4662 - learning_rate: 1.0000e-05 Epoch 77/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 56ms/step - accuracy: 0.8452 - loss: 0.5963 - val_accuracy: 0.9089 - val_loss: 0.4714 - learning_rate: 1.0000e-05 Epoch 78/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8750 - loss: 0.4882 - val_accuracy: 0.9089 - val_loss: 0.4714 - learning_rate: 1.0000e-05 Epoch 79/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 11s 65ms/step - accuracy: 0.8399 - loss: 0.6120 - val_accuracy: 0.9112 - val_loss: 0.4675 - learning_rate: 1.0000e-05 Epoch 80/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8438 - loss: 0.5712 - val_accuracy: 0.9112 - val_loss: 0.4681 - learning_rate: 1.0000e-05 Epoch 81/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 69ms/step - accuracy: 0.8524 - loss: 0.5831 - val_accuracy: 0.9112 - val_loss: 0.4675 - learning_rate: 1.0000e-05 Epoch 82/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8750 - loss: 0.5445 - val_accuracy: 0.9112 - val_loss: 0.4675 - learning_rate: 1.0000e-05 Epoch 83/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 57ms/step - accuracy: 0.8380 - loss: 0.6046 - val_accuracy: 0.9182 - val_loss: 0.4583 - learning_rate: 1.0000e-05 Epoch 84/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 1.0000 - loss: 0.3269 - val_accuracy: 0.9159 - val_loss: 0.4577 - learning_rate: 1.0000e-05 Epoch 85/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 57ms/step - accuracy: 0.8443 - loss: 0.5876 - val_accuracy: 0.9112 - val_loss: 0.4615 - learning_rate: 1.0000e-05 Epoch 86/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8750 - loss: 0.5842 - val_accuracy: 0.9112 - val_loss: 0.4614 - learning_rate: 1.0000e-05 Epoch 87/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 11s 63ms/step - accuracy: 0.8602 - loss: 0.5774 - val_accuracy: 0.9182 - val_loss: 0.4543 - learning_rate: 1.0000e-05 Epoch 88/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8750 - loss: 0.5240 - val_accuracy: 0.9182 - val_loss: 0.4545 - learning_rate: 1.0000e-05 Epoch 89/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 68ms/step - accuracy: 0.8470 - loss: 0.5826 - val_accuracy: 0.9159 - val_loss: 0.4546 - learning_rate: 1.0000e-05 Epoch 90/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.7500 - loss: 0.5967 - val_accuracy: 0.9159 - val_loss: 0.4544 - learning_rate: 1.0000e-05 Epoch 91/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 59ms/step - accuracy: 0.8419 - loss: 0.5936 - val_accuracy: 0.9089 - val_loss: 0.4568 - learning_rate: 1.0000e-05 Epoch 92/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.8438 - loss: 0.7363 - val_accuracy: 0.9089 - val_loss: 0.4569 - learning_rate: 1.0000e-05 Epoch 93/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 56ms/step - accuracy: 0.8568 - loss: 0.5748 - val_accuracy: 0.9159 - val_loss: 0.4500 - learning_rate: 1.0000e-05 Epoch 94/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8750 - loss: 0.5628 - val_accuracy: 0.9136 - val_loss: 0.4503 - learning_rate: 1.0000e-05 Epoch 95/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 69ms/step - accuracy: 0.8435 - loss: 0.5650 - val_accuracy: 0.9089 - val_loss: 0.4559 - learning_rate: 1.0000e-05 Epoch 96/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8750 - loss: 0.4397 - val_accuracy: 0.9089 - val_loss: 0.4566 - learning_rate: 1.0000e-05 Epoch 97/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 7s 56ms/step - accuracy: 0.8494 - loss: 0.5853 - val_accuracy: 0.9159 - val_loss: 0.4459 - learning_rate: 1.0000e-05 Epoch 98/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8125 - loss: 0.6977 - val_accuracy: 0.9159 - val_loss: 0.4453 - learning_rate: 1.0000e-05 Epoch 99/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 56ms/step - accuracy: 0.8537 - loss: 0.5680 - val_accuracy: 0.9136 - val_loss: 0.4468 - learning_rate: 1.0000e-05 Epoch 100/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8438 - loss: 0.4686 - val_accuracy: 0.9136 - val_loss: 0.4471 - learning_rate: 1.0000e-05 Epoch 101/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 11s 65ms/step - accuracy: 0.8569 - loss: 0.5659 - val_accuracy: 0.9159 - val_loss: 0.4444 - learning_rate: 1.0000e-05 Epoch 102/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8750 - loss: 0.3895 - val_accuracy: 0.9182 - val_loss: 0.4440 - learning_rate: 1.0000e-05 Epoch 103/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 8s 65ms/step - accuracy: 0.8567 - loss: 0.5802 - val_accuracy: 0.9136 - val_loss: 0.4399 - learning_rate: 1.0000e-05 Epoch 104/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.8750 - loss: 0.4001 - val_accuracy: 0.9136 - val_loss: 0.4401 - learning_rate: 1.0000e-05 Epoch 105/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 9s 55ms/step - accuracy: 0.8489 - loss: 0.5820 - val_accuracy: 0.9159 - val_loss: 0.4450 - learning_rate: 1.0000e-05 Epoch 106/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8438 - loss: 0.4953 - val_accuracy: 0.9159 - val_loss: 0.4447 - learning_rate: 1.0000e-05 Epoch 107/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 57ms/step - accuracy: 0.8583 - loss: 0.5390 - val_accuracy: 0.9182 - val_loss: 0.4436 - learning_rate: 1.0000e-05 Epoch 108/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.7812 - loss: 0.5868 - val_accuracy: 0.9182 - val_loss: 0.4429 - learning_rate: 1.0000e-05 Epoch 109/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 11s 68ms/step - accuracy: 0.8447 - loss: 0.5989 - val_accuracy: 0.9159 - val_loss: 0.4493 - learning_rate: 1.0000e-05 Epoch 110/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.8125 - loss: 0.5646 - val_accuracy: 0.9159 - val_loss: 0.4493 - learning_rate: 1.0000e-05 Epoch 111/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 7s 57ms/step - accuracy: 0.8375 - loss: 0.6057 - val_accuracy: 0.9136 - val_loss: 0.4417 - learning_rate: 1.0000e-05 Epoch 112/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.9688 - loss: 0.3391 - val_accuracy: 0.9159 - val_loss: 0.4424 - learning_rate: 1.0000e-05 Epoch 113/120 120/120 ━━━━━━━━━━━━━━━━━━━━ 10s 56ms/step - accuracy: 0.8501 - loss: 0.5620 - val_accuracy: 0.9182 - val_loss: 0.4400 - learning_rate: 1.0000e-05 Epoch 113: early stopping End Time: Sat Sep 21 02:29:29 2024 Elapsed seconds: 568.9150 (9.48 minutes)
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.
Saving new model file: model_2.h5 Saved image: accuracy-model_2.png
Saved image: loss-model_2.png
# Capturing learning history per epoch
hist = pd.DataFrame(history2.history)
hist['epoch'] = hist.index + 1
# Create the plot
plt.figure(figsize=(12, 6))
# Plot accuracy
plt.plot(hist['epoch'], hist['accuracy'], label='Train Accuracy')
plt.plot(hist['epoch'], hist['val_accuracy'], label='Validation Accuracy')
# Plot loss
plt.plot(hist['epoch'], hist['loss'], label='Train Loss')
plt.plot(hist['epoch'], hist['val_loss'], label='Validation Loss')
# Add a horizontal line at y=1
plt.axhline(y=1, color='gray', linestyle=':', linewidth=2)
# Customize the plot
plt.title("Train and Validation Losses Over Epochs")
plt.xlabel("Epochs")
plt.ylabel("Accuracy/Loss")
plt.legend(title="Metrics", loc='center left', bbox_to_anchor=(1, 0.5))
plt.grid(True)
# Set y-axis ticks
plt.yticks(np.arange(0, 1.2, 0.2))
plt.tight_layout()
# Show the plot
plt.show()
# Evaluate the model on X_test and y_test
results = model_2.evaluate(X_test_normalized, y_test_encoded)
print(f"Test loss: {results[0]}")
print(f"Test accuracy: {results[1]}")
15/15 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - accuracy: 0.8827 - loss: 0.4906 Test loss: 0.49505603313446045 Test accuracy: 0.87578946352005
# Calculate the accuracy of the model on X_test and y_test
accuracy = model_2.evaluate(X_test_normalized, y_test_encoded, verbose=1)
15/15 ━━━━━━━━━━━━━━━━━━━━ 0s 23ms/step - accuracy: 0.8827 - loss: 0.4906
# Predict on X_test and y_test
y_pred2 = model_2.predict(X_test_normalized)
15/15 ━━━━━━━━━━━━━━━━━━━━ 2s 89ms/step
# Obtaining the categorical values from y_test_encoded and y_pred
y_pred_arg2 = np.argmax(y_pred2, axis=1)
y_test_arg2 = np.argmax(y_test_encoded, axis=1)
# Plotting the Confusion Matrix using confusion matrix() function which is also predefined in tensorflow module
confusion_matrix = tf.math.confusion_matrix(y_test_arg2, y_pred_arg2)
f, ax = plt.subplots(figsize=(12, 12))
sns.heatmap(
confusion_matrix,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
# Setting the labels to both the axes
ax.set_xlabel('Predicted labels');
ax.set_ylabel('True labels');
ax.set_title('Confusion Matrix');
ax.xaxis.set_ticklabels(list(enc.classes_), rotation=40)
ax.yaxis.set_ticklabels(list(enc.classes_), rotation=20)
plt.show()
# Plotting the classification report
cr = metrics.classification_report(y_test_arg2, y_pred_arg2)
print(cr)
precision recall f1-score support
0 0.52 0.50 0.51 26
1 0.86 0.95 0.90 39
2 0.90 0.90 0.90 29
3 0.98 0.90 0.94 61
4 0.91 0.91 0.91 22
5 0.96 0.94 0.95 48
6 0.80 0.80 0.80 65
7 0.91 0.95 0.93 22
8 0.85 0.96 0.90 52
9 0.88 0.61 0.72 23
10 0.96 0.90 0.93 50
11 0.88 1.00 0.94 38
accuracy 0.88 475
macro avg 0.87 0.86 0.86 475
weighted avg 0.88 0.88 0.87 475
Comment on the final model you have selected and use the same in the below code to visualize the image.
Comparing model fig 1 and model fig 2. Fig 2 has the upper hand.
plt.figure(figsize=(2, 2))
plt.imshow(X_test[2])
plt.show()
print(
'Predicted Label', enc.inverse_transform(model_2.predict((X_test_normalized[2].reshape(1, 64, 64, 3))))
) # reshaping the input image as we are only trying to predict using a single image
print(
'True Label', enc.inverse_transform(y_test_encoded)[2]
) # using inverse_transform() to get the output label from the output vector
plt.figure(figsize=(2, 2))
plt.imshow(X_test[33])
plt.show()
# Complete the code to predict the test data using the final model selected
print(
'Predicted Label', enc.inverse_transform(model_2.predict((X_test_normalized[33].reshape(1, 64, 64, 3))))
) # reshaping the input image as we are only trying to predict using a single image
print(
'True Label', enc.inverse_transform(y_test_encoded)[33]
) # using inverse_transform() to get the output label from the output vector
plt.figure(figsize=(2, 2))
plt.imshow(X_test[59], )
plt.show()
print(
'Predicted Label', enc.inverse_transform(model_2.predict((X_test_normalized[59].reshape(1, 64, 64, 3))))
) # reshaping the input image as we are only trying to predict using a single image
print(
'True Label', enc.inverse_transform(y_test_encoded)[59]
) # using inverse_transform() to get the output label from the output vector
plt.figure(figsize=(2, 2))
plt.imshow(X_test[36])
plt.show()
print(
'Predicted Label', enc.inverse_transform(model_2.predict((X_test_normalized[36].reshape(1, 64, 64, 3))))
) # reshaping the input image as we are only trying to predict using a single image
print(
'True Label', enc.inverse_transform(y_test_encoded)[36]
) # using inverse_transform() to get the output label from the output vector
1/1 ━━━━━━━━━━━━━━━━━━━━ 1s 1s/step Predicted Label ['Small-flowered Cranesbill'] True Label Small-flowered Cranesbill
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 40ms/step Predicted Label ['Cleavers'] True Label Cleavers
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 68ms/step Predicted Label ['Common Chickweed'] True Label Common Chickweed
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 82ms/step Predicted Label ['Shepherds Purse'] True Label Shepherds Purse
Model 2 has higher accuracy with roughly 88% the model is good at predicting the type of plants and reducing the time and effort.
Maybe creating an app for agricultural workers to use to identify these plants faster will help in their farming and make decisions more efficiently.